/*
 * @Author: ljk
 * @Date: 2023-05-22 11:57:58
 * @LastEditors: ljk
 * @LastEditTime: 2023-05-22 21:02:53
 * @Description: 可重入函数, volatile, SIGCHLD
 */
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

using namespace std;

//SIGCHLD(不常用)
pid_t id;

// 当有多个子进程同时退出，父进程会收到若干个信号，但同一时间一个进程只能有一个信号被处理，其它的
// 信号就会被阻塞，pending位置为1，然后多个子进程信号表现形式只是一个pending位被置1, 因此可能会导致信号丢失
void waitProcess(int signo)
{
    cout << "get a signal: " << signo << " whoPid: " << getpid() << endl;
    sleep(5);

    while (1)
    {
        //-1 代表回收任意退出的子进程: 只要有退出的子进程，就一直回收
        //如果有部分子进程没有退出完，如果设置为0就会一直阻塞式等待，所以这里应该设置为WNOHANG
        pid_t res = waitpid(-1, NULL, WNOHANG);
        if (res > 0)
        {
            cout << "wait success, res: " << res << " childPid: " << id << endl;
        }
        else break; //如果没有子进程了
    }
    cout << "handler done..." << endl;
}
int main()
{
    //signal(SIGCHLD, waitProcess);

    //完美方式,不会出现僵尸进程 -- 特殊情况不单纯是忽略(检测到这种组合情况, 告诉OS该进程的子进程退出直接回收)
    signal(SIGCHLD, SIG_IGN);
    for (int i = 1; i <= 10; i++)
    {
        id = fork();
        if (id == 0)
        {
            // child
            int cnt = 5;
            while (cnt--)
            {
                cout << "I am child process, pid: " << getpid() << " ppid: " << getppid() << endl;
                sleep(1);
            }
            exit(1);
        }
    }

    // father
    // 父进程无任务，就用以前的方式循环等待
    // 父进程有任务而且很忙，可以选择这种信号等待的方法
    while (1)
    {
        sleep(1);
    }
    return 0;
}

// volatine 演示
// volatile int quit = 0;
// //int quit = 0;

// void handler(int signo)
// {
//     cout << "change quit from 0 to 1" << endl;
//     quit = 1;
// }
// int main()
// {
//     signal(2, handler);

//     while (!quit); //这里什么都不写,故意让编译器认为在main中，quit只会被检测

//     cout << "main quit normal!" << endl;

//     return 0;
// }
